package com.cplatform.movie.back.web;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.cplatform.movie.back.utils.AppConfig;
import com.cplatform.movie.back.utils.JsonRespWrapper;
import com.cplatform.util2.TimeStamp;

@Controller
@RequestMapping("/xheditor/upload")
public class XheditorFileUploadController {

    final Log logger = LogFactory.getLog(this.getClass());
    
	private static final Logger log = Logger.getLogger(XheditorFileUploadController.class);
    
    @Autowired
    AppConfig appConfig;
    
    @RequestMapping(value="remote", method= RequestMethod.POST)
    @ResponseBody
    public Object remote(@RequestParam("urls") String urls) {
        String[] arr = urls.split("\\|");
        String[] outSrc = new String[arr.length];
        for (int i=0; i<arr.length; i++) {
            try {
                outSrc[i] = saveRemoteImage(arr[i]);
            } catch (Exception ex) {
                outSrc[i] = arr[i];
            }
        }
        return StringUtils.join(outSrc, "|");
    }


    
    @RequestMapping(value="{type}", headers="content-type=application/*", produces="application/json", method= RequestMethod.POST)
    @ResponseBody
    public Object upload(@RequestHeader("Content-Disposition") String disp, @PathVariable String type, InputStream is) throws UnsupportedEncodingException {
        Pattern pattern = Pattern.compile("attachment;\\s+name=\"(.+?)\";\\s+filename=\"(.+?)\"");
        Matcher matcher = pattern.matcher(disp);
        if (matcher.find()) {
            String fileName = matcher.group(2);
            return uploadFile(URLDecoder.decode(fileName, "UTF-8"), type, is);
        } else {
            return JsonRespWrapper.uploadError("不正确的上传内容");
        }
    }
    
    
    @RequestMapping(value="{type}", headers="content-type=multipart/*", produces="text/html", method= RequestMethod.POST)
    @ResponseBody
    public Object upload(@RequestParam("filedata") MultipartFile file, @PathVariable String type) throws IOException {
        InputStream is = file.getInputStream();
        String fileName = FilenameUtils.getName(file.getOriginalFilename());
        return uploadFile(fileName, type, is);
    }
    
    private Object uploadFile(String fileName, String type, InputStream is) {
        try {
            checkFileExtention(fileName, type);

            String datePart = TimeStamp.getFormatTime("yyyy/MM/dd");
            String dir = appConfig.getXheditorUploadDir() + datePart;
            String filePart = TimeStamp.getTime(14)
                    + RandomStringUtils.randomAlphanumeric(5)
                    + "."
                    + FilenameUtils.getExtension(fileName);
            
            if (!new File(dir).exists()) {
                new File(dir).mkdirs();
            }
            
            saveToFile(fileName, is, new File(dir + filePart));
            
            return JsonRespWrapper.uploadSuccess(appConfig.getXheditorUploadPath() + datePart + filePart);
        } catch (Exception ex) {
        	log.error(ex.getMessage());
            return JsonRespWrapper.uploadError(ex.getMessage());
        }
    }


    private boolean isAllowImageExt(String ext) {
        return ArrayUtils.contains(appConfig.getXheditorImageExts(), ext);
    }
    
    /*
     * 文件类型检查
     */
    private void checkFileExtention(String fileName, String type) {
        String orgfileExt = FilenameUtils.getExtension(fileName);
        if ("link".equals(type)) {
            if (!ArrayUtils.contains(appConfig.getXheditorExts(), orgfileExt)) {
                String message = String.format("上传的文件 (%s) 类型非法, 我们允许上传的文件后缀为  %s.", 
                        fileName, appConfig.getXheditorExt());
                throw new UploadException(message);
            }
        } else {
            if (!ArrayUtils.contains(appConfig.getXheditorImageExts(), orgfileExt)) {
                String message = String.format("上传的文件 (%s) 类型非法, 我们允许上传的文件后缀为  %s.", 
                        fileName, appConfig.getXheditorImageExt());
                throw new UploadException(message);
            }
        }
    }
    
    // 保存文件
    @SuppressWarnings("resource")
    private void saveToFile(String fileName, InputStream input, File outfile) {
        long maxSize = appConfig.getXheditorMaxSize();
        FileOutputStream output = null;
        
        try {
            output = new FileOutputStream(outfile);
            byte[] buffer = new byte[4096];
            long count = 0;
            int n = 0;
            while (-1 != (n = input.read(buffer))) {
                output.write(buffer, 0, n);
                count += n;
                if (count > maxSize) {
                    String message = String.format("上传的文件 (%s) 超过上传文件大小限制, 最大值为  %s.",
                            fileName, getFormatSize(appConfig.getXheditorMaxSize()));
                    throw new UploadException(message);
                }
            }
        } catch (IOException ex) {
        	log.error(ex.getMessage());
            logger.warn(ex.getMessage(), ex);
            throw new UploadException(String.format("上传的文件 (%s) 上传时发生错误：" + ex.getMessage(), fileName));
        } finally {
            IOUtils.closeQuietly(output);
        }
    }


    private String saveRemoteImage(String constructedUrl) throws Exception {

        if (constructedUrl.startsWith("data:image")) {
            int pstart = constructedUrl.indexOf("/") + 1;
            String orgfileExt = constructedUrl.substring(pstart, constructedUrl.indexOf(';') - pstart).toLowerCase();
            if (!isAllowImageExt(orgfileExt)) {
                throw new UploadException("file extension not allow.");
            }

            String datePart = TimeStamp.getFormatTime("yyyy/MM/dd");
            String dir = appConfig.getXheditorUploadDir() + datePart;
            String filePart = TimeStamp.getTime(14)
                    + RandomStringUtils.randomAlphanumeric(5)
                    + "."
                    + orgfileExt;
            if (!new File(dir).exists()) {
                new File(dir).mkdirs();
            }
            byte[] fileContent = Base64.decodeBase64(constructedUrl.substring(constructedUrl.indexOf("base64,") + 7));
            saveToFile("", IOUtils.toInputStream(new String(fileContent)), new File(dir + filePart));
            return appConfig.getXheditorUploadPath() + datePart + filePart;
        }

        URL url;
        try {
            url = new URL(constructedUrl);
        } catch (final MalformedURLException e) {
            throw new IllegalArgumentException(e);
        }

        HttpURLConnection.setFollowRedirects(false);
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            if(conn.getContentType().indexOf("image")==-1){
                throw new UploadException("remote file is not image");
            }
            if(conn.getResponseCode() != 200){
                throw new RuntimeException("remote file response return error");
            }
            String datePart = TimeStamp.getFormatTime("yyyy/MM/dd");
            String dir = appConfig.getXheditorUploadDir() + datePart;
            String filePart = TimeStamp.getTime(14)
                    + RandomStringUtils.randomAlphanumeric(5)
                    + "."
                    + FilenameUtils.getExtension(constructedUrl);
            if (!new File(dir).exists()) {
                new File(dir).mkdirs();
            }
            saveToFile(FilenameUtils.getName(constructedUrl), conn.getInputStream(), new File(dir + filePart));
            return appConfig.getXheditorUploadPath() + datePart + filePart;
        } catch (final Exception e) {
        	log.error(e.getMessage());
            throw new UploadException(e.getMessage());
        } finally {
            if (conn != null && conn instanceof HttpURLConnection) {
                conn.disconnect();
            }
        }
    }


    private String getFormatSize(double size) {
        double kiloByte = size/1024;
        if(kiloByte < 1) {
            return size + "字节";
        }
        
        double megaByte = kiloByte/1024;
        if(megaByte < 1) {
            BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));
            return result1.setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB";
        }
        
        double gigaByte = megaByte/1024;
        if(gigaByte < 1) {
            BigDecimal result2  = new BigDecimal(Double.toString(megaByte));
            return result2.setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB";
        }
        
        double teraBytes = gigaByte/1024;
        if(teraBytes < 1) {
            BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));
            return result3.setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB";
        }
        BigDecimal result4 = new BigDecimal(teraBytes);
        return result4.setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB";
    }
    
    class UploadException extends RuntimeException {
        private static final long serialVersionUID = 1072880924133226101L;
        public UploadException(String message) {
            super(message);
        }
    }
    
}
